home *** CD-ROM | disk | FTP | other *** search
/ Resource for Source: Assembly / assembly resource for source.iso / assem01 / pop_cal.asm < prev    next >
Assembly Source File  |  1995-11-01  |  23KB  |  378 lines

  1. title POP-CAL - popup calendar for 1583 - 9999 AD.
  2. comment ~
  3.           Use Alt-C, to pop up and terminate calendar generator.
  4.           Use left/right cursor keys to change months.
  5.           Use Up/Down cursor keys to change years.
  6.         ~
  7. cseg     segment
  8.          assume cs:cseg
  9. ;-----------------------------------------------------------------------------
  10. ppfdata         struc           ;Variable data is stored in PPF area.
  11. rom16h          dw      ?,?     ;Holds old Interrupt 16h BIOS vector.
  12. savss           dw      ?       ;Holds caller's stack segment
  13. savsp           dw      ?       ;Holds caller's stack pointer
  14. video_seg       dw      ?       ;Holds base segment of video buffer
  15. mnth            db      ?       ;calendar's month
  16. year            dw      ?       ;calendar's year
  17. weekday         db      ?       ;1st day of month 0=Sun,,,,6=Sat
  18. left            dw      ?       ;Offset to left edge of calendar, row 0
  19. hilite          db      ?       ;Calendar display attribute.
  20. busy            db      ?       ;Busy flag, for our stack
  21. crt_mode        db      ?       ;Holds current crt mode
  22. crt_cols        dw      ?       ;Number of columns on screen
  23. status_port     dw      ?       ;video card status port address
  24. ppfdata         ends
  25.                 org     100h-status_port
  26. v    ppfdata <>                 ;Locate ppfdata and assign prefix "v".
  27. ;-----------------------------------------------------------------------------
  28.         org     100h
  29. begin:  jmp     Install         ;Initialize calendar; go resident.
  30.     db    "Copyright 1986 Ziff-Davis Publishing Co.",1Ah
  31. days    db      'SunMonTueWedThrFriSat'
  32. months  db      'JanFebMarAprMayJunJulAugSepOctNovDec'
  33. numdays db      31,28,31,30,31,30,31,31,30,31,30,31 ;Jan-Dec days/month
  34. bios            dw      40h     ;Points to BIOS data segment
  35. scan_code       dw      2e00h   ;(Alt)(C) scan code to start/stop program.
  36. ;---------------- Intercept keystroke reading --------------------------------
  37. int16h: sti                             ;Turn interrupts back on.
  38.         cmp     ah,0                    ;See if was request for an actual key.
  39.         je      come_back               ;If so, we'll check it first.
  40. skip_us:jmp     dword ptr v.rom16h      ;Otherwise, let go straight to caller.
  41. come_back:
  42.         cmp     v.busy,0                ;Is calendar routine busy?
  43.         jne     skip_us                 ;If so, pass all keys along.
  44. getkey: pushf                           ;Else, call BIOS keyboard routine,
  45.         call    dword ptr v.rom16h      ;so it will return the key to us.
  46.         cmp     ax,scan_code            ;Is scan code correct?
  47.         je      now_busy                ;If so, go to work.
  48.         iret                            ;Else just pass on key to caller.
  49. now_busy:
  50.         mov     v.busy,1                ;Set busy flag, to protect our stack.
  51.         mov     v.savss,ss              ;Save caller's stack segment.
  52.         mov     v.savsp,sp              ;save caller's stack pointer.
  53.         mov     sp,cs
  54.         cli                             ;Avoid interrupt right now.
  55.         mov     ss,sp                   ;Reset stack to our code segment.
  56.         mov     sp,offset v.rom16h      ;Start our stack below ROM16 addr.
  57.         sti
  58.         push    ax
  59.         push    bx
  60.         push    cx
  61.         push    dx
  62.         push    bp
  63.         push    si                      ;Save all user's registers.
  64.         push    di
  65.         push    ds
  66.         push    es
  67.         call    begin_calendars         ;Go do the calendars.
  68.         pop     es                      ;When finished,...
  69.         pop     ds
  70.         pop     di
  71.         pop     si
  72.         pop     bp                      ;Restore all user's registers
  73.         pop     dx
  74.         pop     cx
  75.         pop     bx
  76.         pop     ax
  77.         cli
  78.         mov     ss,v.savss              ;restore old stack
  79.         mov     sp,v.savsp
  80.         sti
  81.         mov     ah,0                    ;Reset AH to BIOS key read function.
  82.         mov     v.busy,0                ;Allow reuse of calendar stack.
  83.         jmp     getkey                  ;Go get new key from BIOS.
  84. ;---------------  Read keys input.  Process calendars.  ----------------------
  85. begin_calendars:
  86.         push    cs
  87.         push    cs
  88.         pop     ds              ;Set DS and ES to our code segment.
  89.         pop     es
  90.         cld                     ;Set for standard "up" direction.
  91.         assume  ds:cseg         ;Let Assembler use DS addressing, now.
  92.         call    wndo            ;Save Screen data. Put old calendar on it.
  93.         jmp     showcal         ;Go fill in calendar, in case none made yet.
  94. nxtcal: mov     v.mnth,dh       ;Store new month value.
  95.         mov     v.year,cx       ;Store new year.
  96. showcal:call    fill            ;Format and display new calendar.
  97. input:  mov     ah,0            ;Wait for key stroke.
  98.         int     16h
  99.         cmp     ax,scan_code    ;Another (Alt)(C)?
  100.         jne     getdate         ;If not, initialize CX (year) and DH (month).
  101. exit:   call    wndo            ;Restore original screen data and
  102.         ret                     ;  go restore caller's stack... all done.
  103. getdate:mov     dh,v.mnth       ;Load calendar month into DH.
  104.         mov     cx,v.year       ;Load calendar year into CX.
  105. trylft: cmp     ah,75           ;Was key a left arrow?
  106.         jne     tryrgt          ;If not, go see if was a right arrow.
  107.         dec     dh              ;  If a left arrow, reduce month by 1.
  108.         jns     nxtcal          ;  When pass Jan, switch
  109.         mov     dh,11           ;  to Dec of prior year.
  110.         dec     cx              ;  Reduce year by one.
  111.         jmp     chkloyr         ;  Go check that its still in range.
  112. tryrgt: cmp     ah,77           ;Right arrow? (increase month)
  113.         jne     tryup           ;If not, see if an Up arrow.
  114.         inc     dh              ;  Was right arrow, so increase month.
  115.         cmp     dh,11           ;  See if past December.
  116.         jng     nxtcal          ;  If not, go make the calendar.
  117.         mov     dh,0            ;  If was, reset month to January
  118.         inc     cx              ;  and move up to next year.
  119.         jmp     chkhiyr         ;  Then insure year is still in range.
  120. tryup:  cmp     ah,72           ;Up arrow?  (increase year)
  121.         jne     trydwn          ;If not, see if was a down key.
  122.         inc     cx              ;   If Up, increase year by 1.
  123. chkhiyr:cmp     cx,10000        ;   Year can only be 4 digits.
  124.         jge     input           ;   Ignore key, if date goes out of range.
  125.         jmp     nxtcal          ;   Otherwise go make new calendar.
  126. trydwn: cmp     ah,80           ;Down arrow? (decrease year)
  127.         jne     input           ;If not, ignore key and go get new one.
  128.         dec     cx              ;   Down key reduces year by 1.
  129. chkloyr:cmp     cx,1582         ;Calendar calculations only good for 11/1582
  130.         jle     input           ;   on, so ignore key, if year now < 1583.
  131.         jmp     nxtcal          ;   Otherwise, go make new calendar.
  132. ;------------------  Fill in the new calendar   ------------------------------
  133. fill:   call    firstday        ;Calculate weekday of 1st of month.
  134.         mov     es,v.video_seg  ;Point ES to write to screen.
  135.         mov     al,v.mnth       ;Put current month into AL.
  136.         cbw                     ;Zeros out AH.
  137.         mov     cx,3            ;Zero out CH. Put 3 in CL.
  138.         mul     cl              ;AX = # bytes to current month's name
  139.         mov     si,offset months;   from start of "months".
  140.         add     si,ax           ;SI=offset to month's name
  141.         mov     di,v.crt_cols   ;Put screen width (# columns) in DI.
  142.         add     di,v.left       ;Move to left edge, second row.
  143.         add     di,22           ;Move on to center month in the row.
  144. movname:lodsb                   ;Load character from month's name.
  145.         call    writebyte       ;Output char of month's name to screen.
  146.         loop    movname         ;Do three times, since cx=3.
  147.         add     di,2            ;Skip a space, after the month's name.
  148.         mov     ax,v.year       ;Load year into AX.
  149.         mov     bl,100          ;Will use to split year into centuries
  150.         div     bl              ;  portion and remaining years in century.
  151.         mov     bh,1            ;Set flag, so will retain leading zero.
  152.         call    unpack          ;Display the centuries portion of year.
  153.         mov     al,ah           ;Put remainder in al.
  154.         call    unpack          ;Display remaining 2 digits in year.
  155. setdi:  mov     ax,v.crt_cols   ;Load current screen width.
  156.         mov     bl,4            ;Mult by # rows to skip.
  157.         mul     bl              ;Calculate offset to 4th row of calendar.
  158.         add     ax,4            ;Skip in 2 columns on that row.
  159.         mov     di,v.left       ;Set to offset of top left corner of calendar.
  160.         add     di,ax           ;Move to location of 1st date on screen.
  161.         mov     dl,v.weekday    ;Retrieve week day of 1st of cal's month.
  162.         mov     cl,dl           ;Store as count of days to blank in 1st week.
  163.         mov     dh,6            ;DH = # weeks left to fill in.
  164.         jcxz    start_current_month ;If Sunday, CX = 0 and no blanking needed.
  165. blnkrw1:call    blank2          ;Blank out 1st part of 1st week.
  166.         loop    blnkrw1
  167. start_current_month:
  168.         mov     cl,7            ;Number of days to display in 1st week
  169.         sub     cl,dl           ;   = 7 - weekday of 1st day.
  170.         mov     bl,v.mnth       ;Set index for days-per-month array.
  171.         xor     bh,bh           ;Set flag to suppress leading zeros.
  172.         mov     dl,numdays[bx]  ;Get # days in current month.
  173.         mov     al,1            ;Set AL to 1st day of month.
  174. showdays:
  175.         call    unpack          ;Format and display the date value.
  176.         inc     al              ;Increment to next day.
  177.         add     di,4            ;Skip to next day position.
  178.         dec     dl              ;Reduce # days left in month.
  179.         jz      blnkrst         ;If last day, blank rest of the 6 weeks.
  180.         loop    showdays        ;Otherwise continue displaying dates in week.
  181.         call    nxtweek         ;When reach end of a week, space to next one.
  182.         jmp     showdays        ;  and continue on new row of calendar.
  183. blnkrw4:call    blank2          ;Blank out remaining days in week.
  184. blnkrst:loop    blnkrw4         ;Blank to end of current week.
  185.         call    nxtweek         ;Relocate DI to 1st day of next week.
  186.         jnz     blnkrw4         ;If not past last week, go blank week out.
  187.         push    cs              ;When all done, restore ES to CS.
  188.         pop     es
  189.         ret                     ;Return to keyboard reading routine.
  190. ;---------------------- Move DI to 1st date in next week ---------------------
  191. nxtweek:add     di,v.left       ;Move to left edge of next row of calendar.
  192.         add     di,4            ;Move in an additional 2 columns.
  193.         mov     cl,7            ;Restart week count.
  194.         dec     dh              ;Reduce weeks left in calendar.
  195.         ret
  196. ;---------------------- Compute weekday of 1st day of month ------------------
  197. firstday:
  198.         mov     si,v.year               ;Hold year in SI, of quick access.
  199.         mov     numdays[1],28           ;Assume this isn't a leap year.
  200.         test    si,3                    ;Now see if year divisible by 4.
  201.         jnz     getday                  ;If not, was actually not a leap year.
  202. feb29:  mov     numdays[1],29           ;Else was leap year with 29 days.
  203. getday: mov     bx,6                    ;BX=Saturday,(weekday of 1/1/1583)
  204.         mov     ax,si                   ;Calendar year to AX.
  205.         sub     ax,1583                 ;Calc. # years since our base year.
  206.         add     bx,ax                   ;Calendar advances 1 weekday per
  207.                                         ;  year unless leap years intervene.
  208.         add     ax,2                    ;Adj diff, so even #, if the calendar
  209.         mov     cx,4                    ;  year was year after a leap year.
  210.         cwd                             ;Zero out DX.
  211.         div     cx                      ;Calculate # leap years before this.
  212.         add     bx,ax                   ;Add 1 day to week, for each leap yr.
  213.         cmp     si,1700                 ;Things work normally 'til 1700, which
  214.         jl      cnvrt                   ;not a leap year, per Gregorian cal.
  215.         mov     ax,si                   ;Year back to AX, again.
  216.         sub     ax,1600                 ;Get # years since 1600.
  217.         mov     cl,100
  218.         cwd                             ;Convert to number of centuries.
  219.         div     cx
  220.         cmp     dx,0                    ;If remainder=0 this is centennial yr.
  221.         jne     deduct
  222.         test    ax,3                    ;If century is evenly divisible by
  223.         jz      decax                   ; 400, it is also still a leap year.
  224.         mov     numdays[1],28           ;Other centenial years have 28 days.
  225. decax:  dec     ax                      ;If centenial yr, sub 1 from centuries
  226. deduct: sub     bx,ax                   ;Subtract 1 weekday per century.
  227.         cwd
  228.         mov     cl,4
  229.         div     cx                      ;Calculate centuries mod 400.
  230.         add     bx,ax                   ;Add back 1 day per 400 years.
  231. cnvrt:  mov     si,offset numdays       ;Add in the days per month, this yr.
  232.         xor     ah,ah                   ;First clear hi byte of accumulator
  233.         mov     cl,v.mnth               ;Load cx with month counter
  234.         jcxz    final                   ;If January, are ready for final calc.
  235. addmnth:lodsb                           ;Get low byte into accumulator.
  236.         add     bx,ax                   ;Add to days count.
  237.         loop    addmnth                 ;Continue until prior months added in.
  238. final:  mov     ax,bx                   ;AX=# days advanced since 1/1/1583.
  239.         cwd                             ;Clear out DX.
  240.         mov     cl,7                    ;Find # full weeks since 1583.
  241.         div     cx                      ;DL=weekday of 1st of calendar's month
  242.         mov     v.weekday,dl            ;Save results of this work.
  243.         ret
  244. ;-------------- Convert binary # in AL to ASCII and output to ES:DI ----------
  245. unpack: push    ax              ;(AL should binary # from 0-99.)
  246.         push    bx
  247.         aam                     ;Divide al by 10.
  248.         add     ax,3030h        ;Convert digits to ASCII.
  249.         cmp     ah,30h          ;See if 10's digit was a zero.
  250.         jne     putnum          ;If not, go output it to screen.
  251.         cmp     bh,1            ;If BH=1, leading zero is ok, so
  252.         je      putnum          ;   go write it.  Otherwise, replace
  253.         mov     ah,20h          ;   leading zero with a blank.
  254. putnum: mov     bl,al           ;Need to write high order digit first,
  255.         mov     al,ah           ;  so save low digit and move hi into AL.
  256.         call    writebyte       ;Output 10's digit to screen.
  257.         mov     al,bl           ;Put 1's digit back in AL.
  258.         call    writebyte       ;Write 1's digit.
  259.         pop     bx              ;Restore BX.
  260.         pop     ax              ;Retrieve old binary value.
  261.         ret                     ;All done.
  262. ;------------------- Blank out 2 digit calendar date field.  Update DI. ------
  263. blank2: mov     al,20h          ;Put a blank in AL
  264.         call    writebyte       ;Write a blank over 1st digit.
  265.         call    writebyte       ;Write a blank over 2nd digit.
  266.         add     di,4            ;Skip 2 spaces, to next date.
  267.         ret
  268. ;------------------- Write char AL on screen at ES:DI. Update DI. ------------
  269. writebyte:
  270.         push    dx                      ;Save DX.
  271.         mov     dx,v.status_port        ;Reset to video status port address.
  272.         mov     ah,al                   ;Move character to AH, for now.
  273. on1:    in      al,dx                   ;Wait until not doing a
  274.         test    al,1                    ;  horizontal scan retrace.
  275.         jnz     on1
  276.         cli
  277. on2:    in      al,dx                   ;Now wait until next horizontal
  278.         test    al,1                    ;  retrace begins, so KNOW will have
  279.         jz      on2                     ;  entire retrace time period to work.
  280.         mov     al,ah                   ;Retrieve character from AH.
  281.         stosb                           ;Store character on screen.
  282.         sti
  283.         inc     di                      ;Skip over attribute byte on screen.
  284.         pop     dx                      ;Restore old DX value.
  285.         ret
  286. ;---------------------- Switch memory field with screen data -----------------
  287. wndo:   call    getprms                 ;Get current video parms
  288.         mov     dx,v.status_port        ;Point DX to Status Port.
  289.         sub     dx,2                    ;Back up to control port.
  290.         mov     es,bios                 ;Point to ROM BIOS data segment
  291.         mov     bh,es:[65h]             ;Get current setting of video card.
  292.         mov     al,bh                   ;Move to AL, for output, after reset.
  293.         and     al,0f7h                 ;Turn off video enable bit only.
  294.         out     dx,al                   ;Turn off screen.
  295.         mov     si,offset data_strt     ;Point to calendar data in memory.
  296.         mov     di,v.left               ;Set DI=upper left corner of calendar.
  297.         mov     es,v.video_seg          ;Set ES to video buffer segment.
  298.         mov     bl,11                   ;11 rows in the calendar.
  299. nxtlin: mov     cx,30                   ;There are 30 words per calendar row.
  300. getchr: mov     ax,es:[di]              ;Get next word from screen buffer.
  301.         movsw                           ;Move data word to screen.
  302.         mov     [si-2],ax               ;Store screen character in our data.
  303.         loop    getchr                  ;Continue across row.
  304.         add     di,v.left               ;Move to start of next calendar row.
  305.         dec     bl                      ;Reduce rows-to-go count.
  306.         jnz     nxtlin                  ;Do next row, if more to go.
  307.         mov     al,bh                   ;Restore video setting to original
  308.         out     dx,al                   ;  and turn on screen again.
  309.         push    cs
  310.         pop     es                      ;Restore ES to our code segment.
  311.         ret
  312. ;------------------- Get video display parameters ----------------------------
  313. getprms:push    es
  314.         mov     es,bios                 ;Point ES to BIOS data segment.
  315.         mov     ax,es:[4ah]             ;Put current column width.
  316.         shl     ax,1                    ;Multiply by 2, for attribute bytes.
  317.         mov     v.crt_cols,ax           ;Store in our data, for easy access.
  318.         sub     al,30*2                 ;Back off from right side, by width of
  319.         mov     v.left,ax               ;calendar. Equals offset to left edge.
  320.         mov     ax,es:[63h]             ;Get base address of video card.
  321.         add     ax,6                    ;Calculate status port address and
  322.         mov     v.status_port,ax        ;  save for checking horiz scans.
  323.         mov     v.hilite,70h            ;Black on white background.
  324.         mov     bl,es:[49h]             ;Get video mode into BL.
  325.         cmp     bl,7                    ;In Monochrome video mode?
  326.         mov     ax,0B000h               ;Monochrome buffer seg at 0B000h.
  327.         je      setseg                  ;If monochrome, go store video seg.
  328.         mov     ax,0B800h               ;If graphics card, video seg=0B800h.
  329.         test    bl,1                    ;1,3 are color text on graphics card.
  330.         jz      setseg
  331.         mov     v.hilite,1fh            ;Use bright white on blue, if color.
  332. setseg: mov     v.video_seg,ax          ;Store video_segment value
  333.         pop     es                      ;Restore Es.
  334.         ret                             ;Done.
  335. ;------------------ Create calendar form and become resident. ---------------
  336. Install:call    getprms                 ;Call, to get correct color attribute.
  337.         mov     di,offset data_strt     ;Point to end of our code segment.
  338.         mov     ah,v.hilite             ;Set attribute byte in AH.
  339.         mov     al,20h                  ;Will store blanks in calendar area.
  340.         mov     cx,11*30                ;11 rows of 30 columns each.
  341.         rep     stosw                   ;Blank out calendar work area
  342.         push    di                      ;Save end of calendar offset.
  343.         mov     si,offset days          ;Now store day's names in calendar.
  344.         mov     bl,7                    ;Set names-to-go counter to 7.
  345.         mov     di,offset data_strt     ;Point to start of calendar
  346.         add     di,3*30*2+4             ;Skip in 3 rows + 2 columns.
  347. movday: mov     cx,3                    ;There are 3 chars per name.
  348. movchr: movsb                           ;Transfer character into calendar.
  349.         inc     di                      ;Skip over attribute byte.
  350.         loop    movchr                  ;Continue for three characters.
  351.         inc     di
  352.         inc     di                      ;Add 2 to DI, to skip space.
  353.         dec     bl                      ;Reduce names-to-go count.
  354.         jnz     movday                  ;Continue until all stored.
  355.         mov     ah,2ah                  ;Get current date from DOS.
  356.         int     21h
  357.         dec     dh                      ;Convert months,so Jan=0, etc.
  358.         mov     v.mnth,dh               ;Save month and year values.
  359.         mov     v.year,cx
  360. go_resident:
  361.         xor     ax,ax
  362.         mov     es,ax                   ;Set ES to 0.
  363.         mov     ax,es:[16h*4]
  364.         mov     v.rom16h,ax             ;Store rom interrupt 16h address.
  365.         mov     ax,es:[16h*4+2]
  366.         mov     v.rom16h+2,ax
  367.         mov     ax,offset int16h
  368.         cli
  369.         mov     es:[16h*4],ax           ;Point Interrupt 16 to our code.
  370.         mov     es:[16h*4+2],cs
  371.         sti
  372.         pop     dx                      ;Retrieve address of end of data.
  373.         int     27h                     ;Now become resident.
  374. ;----------------- Calendar data area  --------------------------------
  375. data_strt        equ     this word
  376. cseg    ends
  377.         end     begin
  378.